﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Threading;
using CI.Mesopotamia;
using CI.PW.ViewModels;

namespace CI.PW
{
    /// <summary>
    /// Interaction logic for Display.xaml
    /// </summary>
    public partial class Display
    {

        internal static string ScenarioName
        {
            get { return Bootstrap.ScenarioName; }
            set { Bootstrap.ScenarioName = value; }
        }

        internal static List<Guid> SelectedOrganismsToLoad
        {
            get { return Bootstrap.SelectedOrganismsToLoad; }
        }

        private bool _isActive;
        private Point _mousePosition;
        private bool _externalWindowHidden = true;

        private const string RuntimeArgs = "/runtime:";

        /// <summary>
        /// Simulation Environment
        /// </summary>
        private readonly SimEnv _simEnv;

        private readonly Dispatcher _uiDispatcher;

        public static PWBootStrap Bootstrap;


        private static readonly RoutedUICommand ExitCommand = new RoutedUICommand("Exit", "ExitApp", typeof(Display));
        private static readonly RoutedUICommand PauseCommand = new RoutedUICommand("Pause", "PauseApp", typeof(Display));
        public static RoutedUICommand ExitApp { get { return ExitCommand; } }
        public static RoutedUICommand PauseApp { get { return PauseCommand; } }

        /// <summary>
        /// ctor
        /// </summary>
        public Display(SimEnv simEnv, PWBootStrap bootStrap, bool wasRestart)
        {
            _simEnv = simEnv;
            _uiDispatcher = Dispatcher.CurrentDispatcher;

            _simEnv.TraceLogger.LogDebug("Display ctor");

            bootStrap.ProgressWindow = new Progress();

            Bootstrap = bootStrap;

            // set error handling
            // wire up the test excpetion handler
            AppDomain.CurrentDomain.UnhandledException += _simEnv.ExceptionHandler;
            Dispatcher.UnhandledException += DispatcherUnhandledException;

            InitializeComponent();

#if DEBUG
            TestCrash.Visibility = Visibility.Visible;
#endif

            SetupCommandBindings();

            _simEnv.ServiceExit += () => Exit(false);

            _simEnv.TraceLogger.LogDebug("Components initialized");

            MenuPause.IsChecked = _simEnv.PauseState;
            _simEnv.OnPauseEvent += SimEnvOnPauseEvent;
            _simEnv.OnMainLoopStartEvent += OnMainLoopStartEvent;
            bootStrap.ManifestStarted += ManifestLoaded;

            Closing += ExitApp_Handler;

            Indicitive.IsChecked = true;
            ((NNetViewModel)nnWPF.DataContext).NeuralNetworkDisplayMode = NeuralNetworkDisplayMode.Indicitive;

            // if the screen saver is loaded and loaded this
            CheckLaunchMode();

            _simEnv.StartTime = GetRunTime();

            // make sure where not already running            
            if (Globals.IsAlreadyRunning())
            {
                if (Globals.LaunchedFrom != LaunchType.ScreenSaver)
                    MessageBox.Show("Mesopotamia is already running", "Error", MessageBoxButton.OK);

                bootStrap.StopRoboticsEngine();

                // exit
                program.Exit(0);
            }

            _simEnv.TraceLogger.LogDebug(string.Format("Launched From {0}", Globals.LaunchedFrom));

            // if loaded from gui
            if (Globals.LaunchedFrom == LaunchType.Gui && !wasRestart)
            {
                // start the new wizard
                MenuItem_NewClick(null, null);
            }


            Bootstrap.StartTrigger.Set();

            SetTitleScenarioName();

            _simEnv.TraceLogger.LogDebug("Display -End");

        }

        internal void SetTitleScenarioName()
        {
            Title = string.IsNullOrEmpty(ScenarioName) ? "Mesopotamia : Loading" : "Mesopotamia : " + ScenarioName;
        }

        private DateTime GetRunTime()
        {
            var startTime = DateTime.Now;

            foreach (var commandLineArg in Environment.GetCommandLineArgs())
            {
                if (commandLineArg.Contains(RuntimeArgs))
                {
                    startTime = new DateTime(long.Parse(commandLineArg.Replace(RuntimeArgs, "")));
                }
            }

            return startTime;
        }

        private void CheckLaunchMode()
        {
            if (IsScreenSaverMode())
            {
                // set launch as screen saver
                Globals.LaunchedFrom = LaunchType.ScreenSaver;
                // add handlers
                Loaded += OnLoadedScreenSaver;
                // start the app minimized until it is running
                WindowState = WindowState.Minimized;
                // set it to use max area with no toolbar etc
                ResizeMode = ResizeMode.NoResize;
                WindowStyle = WindowStyle.None;
                // hide the menu bar
                MainMenu.Visibility = Visibility.Collapsed;
            }
            else
            {
                // else we loaded from normal gui
                Globals.LaunchedFrom = LaunchType.Gui;

                // set the screen saver
                // todo : add method to detect and prompt user if not set, also not to nag them each time
                if (!ScreenSaver.AreWeCurrentScreenSaver())
                {
                    ScreenSaver.SetScreenSaver();
                }
            }
        }

        private static bool IsScreenSaverMode()
        {
            bool isScreenSaverMode = false;
            if (Environment.GetCommandLineArgs().Any(commandLineArg => commandLineArg.Equals("/s")))
            {
                isScreenSaverMode = true;
            }

            return isScreenSaverMode;
        }

        private void DispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
        {
            _simEnv.TraceLogger.LogException("Exception In Dispatcher", e.Exception);
            e.Handled = true;
        }

        void OnMainLoopStartEvent()
        {
            // todo : put this somewhere else, earlier?
            _uiDispatcher.BeginInvoke(DispatcherPriority.Background, (Action)
                                            delegate
                                            {
                                                if (MenuPause.Visibility == Visibility.Collapsed)
                                                    MenuPause.Visibility = Visibility.Visible;
                                            });
        }

        private void SimEnvOnPauseEvent(bool state)
        {

            _uiDispatcher.BeginInvoke(DispatcherPriority.Background, (Action)delegate
                                            {
                                                MenuPause.IsChecked = state;
                                            });
        }

        private void ManifestLoaded()
        {
            // set display control
            // todo : potential bug we could keep adding them?
            _uiDispatcher.BeginInvoke(DispatcherPriority.Background,
                                            (Action)(delegate
                                                                {
                                                                    if (_simEnv.HostEnvPhysical.DisplayControl != null)
                                                                        uGrid.Children.Add(_simEnv.HostEnvPhysical.DisplayControl);
                                                                }));

            // setup main menu
            _uiDispatcher.BeginInvoke(DispatcherPriority.Background, (Action)delegate
                         {
                             HideExternalWindow.Visibility = Visibility.Visible;
                             HideExternalWindow.IsChecked = _externalWindowHidden;
                             // if running in screen saver mode maximise it
                             if (Globals.LaunchedFrom == LaunchType.ScreenSaver)
                             {
                                 WindowState = WindowState.Maximized;
                             }
                         });
        }

        private void SetupCommandBindings()
        {
            _simEnv.TraceLogger.LogDebug("Setting command bindings");

            // add a key gesture, ctrl-x for exit
            CommandBindings.Add(new CommandBinding(ExitApp, ExitApp_Handler));
            ExitApp.InputGestures.Add(new KeyGesture(Key.X, ModifierKeys.Control));

            // add space bar for pause
            CommandBindings.Add(new CommandBinding(PauseApp, PauseToggle));
            PauseApp.InputGestures.Add(new KeyGesture(Key.Space));

        }

        /// <summary>
        /// Exit the app
        /// </summary>
        private void Exit(bool forceCleanExit)
        {
            if (forceCleanExit)
                _simEnv.ForceCleanExit = true;

            // turn off nnet displaying
            _uiDispatcher.Invoke(DispatcherPriority.Send, (Action)delegate
            {
                // todo : change foreach child in grid dispose.
                ((NNetViewModel)nnWPF.DataContext).Dispose();
                ((StatusControlViewModel)statusControl.DataContext).Dispose();

                WindowState = WindowState.Minimized;

                if (_simEnv.HostEnvPhysical != null)
                    ((IView)_simEnv.HostEnvPhysical.DisplayControl.DataContext).Dispose();

                _simEnv.TriggerApplicationExit();

                // shutdown start
                _simEnv.TraceLogger.LogDebug("Exit command recieved - PW " + DateTime.Now.ToLocalTime());

                // shutdown
                _simEnv.TraceLogger.LogDebug("Shutdown Mesopotamia - PW " + DateTime.Now.ToLocalTime());
            });


        }

        #region ScreenSaverHandlers
        void OnLoadedScreenSaver(object sender, EventArgs e)
        {
            Topmost = true;
            WindowState = WindowState.Minimized;
            MouseMove += Main_MouseMove;
            KeyDown += Main_ScreenSaverKill;
            MouseDown += Main_ScreenSaverKill;
        }

        /// <summary>
        /// Kill the screen saver
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void Main_ScreenSaverKill(object sender, EventArgs e)
        {
            // close app 
            _simEnv.TraceLogger.LogDebug("Screen Saver detected change - exit");
            Exit(true);
        }

        /// <summary>
        /// Kill the screen saver
        /// </summary>
        void Main_MouseMove(object sender, MouseEventArgs e)
        {
            var currentPosition = e.GetPosition(this);

            // Set IsActive and MouseLocation only the first time this event is called.
            if (_isActive == false)
            {
                _mousePosition = currentPosition;
                _isActive = true;
            }
            else
            {
                // If the mouse has moved significantly since first call, close.
                if ((Math.Abs(_mousePosition.X - currentPosition.X) > 50) ||
                    (Math.Abs(_mousePosition.Y - currentPosition.Y) > 50))
                {
                    // stop, screen saver moved the mouse
                    _simEnv.TraceLogger.LogDebug("Screen Saver detected change - exit");
                    Exit(true);

                }
            }
        }
        #endregion

        #region Event Handlers
        /// <summary>
        /// Command hanlder to exit application
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ExitApp_Handler(object sender, EventArgs e)
        {
            Exit(true);
        }

        #endregion

        #region Menu handlers
        /// <summary>
        /// Show the about click
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void MenuItem_AboutClick(object sender, RoutedEventArgs e)
        {
            var aboutBox = new AboutBox();
            aboutBox.ShowDialog();
        }


        private void MenuItemSemwClick(object sender, RoutedEventArgs e)
        {
            // toggle bit
            _externalWindowHidden ^= true;
            HideExternalWindow.IsChecked = _externalWindowHidden;
            // show hide
            if (_simEnv.HostEnvPhysical != null)
                _simEnv.HostEnvPhysical.HideExternalCameraWindow(_externalWindowHidden);
        }

        private void PauseToggle(object sender, RoutedEventArgs e)
        {
            _simEnv.TogglePause();
        }

        /// <summary>
        /// Start the new sim/robot
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void MenuItem_NewClick(object sender, RoutedEventArgs e)
        {
            // create a new navigational window
            var navWindow = new System.Windows.Navigation.NavigationWindow { Title = "Welcome" };

            // start it at the new page
            var newService = new NewService();
            navWindow.Content = newService;

            // go modal
            navWindow.ShowDialog();

            navWindow.Title = string.Empty;
            navWindow.Close();
        }

        #endregion

        private void SetNNetVisibilty(object sender, RoutedEventArgs e)
        {

            NeuralNetworkDisplayMode neuralNetworkDisplayMode;

            ClearNNetDrawMenuChecks();

            switch (((MenuItem)sender).Header.ToString())
            {
                case "Off":
                    neuralNetworkDisplayMode = NeuralNetworkDisplayMode.Off;
                    Off.IsChecked = true;
                    break;
                case "Indicitive":
                    neuralNetworkDisplayMode = NeuralNetworkDisplayMode.Indicitive;
                    Indicitive.IsChecked = true;
                    break;
                case "Real Time":
                    neuralNetworkDisplayMode = NeuralNetworkDisplayMode.RealTime;
                    RealTime.IsChecked = true;
                    break;
                default:
                    throw new NotImplementedException("Unknown option");
            }

            var nNetViewModel = nnWPF.DataContext as NNetViewModel;
            if (nNetViewModel != null)
                nNetViewModel.NeuralNetworkDisplayMode = neuralNetworkDisplayMode;
        }

        private void ClearNNetDrawMenuChecks()
        {
            Off.IsChecked = false;
            Indicitive.IsChecked = false;
            RealTime.IsChecked = false;
        }

        private void MenuItemExceptionClick(object sender, RoutedEventArgs e)
        {
            throw new Exception("Test crash");
        }


        private void MenuItemRestartClick(object sender, RoutedEventArgs e)
        {
            _simEnv.SetPause(false);
            const string message = "User request restart";
            _simEnv.HostEnv.ProcessingState = message;
            _simEnv.TriggerScenarioRestart(message);
        }
    }

}
